home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-03-06 | 9.6 KB | 271 lines | [TEXT/GEOL] |
- Item 8439715 8-Nov-88 17:39
-
- From: MACDTS Macintosh Developer Technical Supt.
-
- To: D1736 Neurodata, Dev, Robert Norman
-
- cc: MACAPP.TECH$ MACAPP Tech
-
- Sub: Sound advice
-
- TWIMC,
-
- There are a number of problems in the code I received from you. I'm only
- teasing you when I say this, but for 9 lines of code there were many bugs.
- Although its not totally your fault, the Sound Manager chapter of Inside Mac V
- is lame. All certified developers will recieve a replacement chapter from
- yours truly in the Dec. mailings.
-
- * You're creating a sound channel immediately at the programs start up and
- holding on to it for the life of the application. Normally this is considered
- unfriendly. If you are actually using sound throughout the life of the program
- and using it at all times, then this is a valid thing to do. But also realize
- that no other application will be able to produce sound while you're running.
- This includes _SysBeep.
-
- * You don't need LoadResource after calling GetNamedResource.
-
- * After calling GetNamedResource you don't have to lock it for SndPlay, but if
- you are using the sound continuously this might be a good idea. But, if this
- sound is large you may find your getting into memory problems. You should add
- a HMoveHi before the HLock. Also, after a HLock you don't need the HNoPurge.
- Also, make sure your snd resources have their "purgable" bit set, or you add
- HPurge after the HUnlock.
-
- * By the sound of your snd description, your creating a wave table. Since the
- waveTable is broken on the Mac Plus/SE, then go ahead and play the wave table
- with the sampledSynth. This waveTable need only be 512 bytes. You should add
- in loop points for the sampledSynth.
-
- * You are using a snd resource that probably specifies "which synth" and also
- contains additional synth information. Every call to SndPlay will attempt to
- allocate more synth memory and adds that synth to the channel as a modifier.
- The first call to SndPlay got you into trouble. Every call after made it even
- worse.
-
- * You should install the sampled sound buffer into the channel and then send
- note commands instead. I'll send you my Pascal routines that I've written to
- do this.
-
-
- The code below is what I use to play snd resources. The snd must specify which
- synth is require to produce the sound with my AsynchSndPlay routine. I first
- open a channel without linking it to a synth. Then call SndPlay. The global
- flag "gCallBackCalled" will be set to TRUE once the sound is done. The
- procedure "AsynchSndPlay" can be called at any time. It will first dispose of
- the channel if it is in use. You could also keep the channel open and use the
- flushCmd and quietCmd with SndDoImmediate. This is exactly what
- SndDisposeChannel does. If you are only using one synthesizer, then go ahead
- and keep it open. But, don't keep a sound channel open longer than you have
- to. Open it, make the sound, and then dispose of it as soon as you're done.
- There is very little overhead in SndNewChannel and SndDisposeChannel.
-
- The callBack procedure is necessary when playing sound asynchronously. The
- main event loop can test for the global flag "gCallBackCalled" and then dispose
- of the channel and release any locked resources. If you don't like this
- method, you can get to the sampled sound data yourself using some pointer math.
- You need to locate the sampled sound buffer in the resource and then locked it
- into memory. Pass the pointer to this buffer and use the bufferCmd. This will
- allow you to keep the channel open and send it as many bufferCmds you'd like.
- You could also use the soundCmd, which will install the sample as an
- instrument. Then you could play a melody with that sound using note and rest
- commands.
-
- My ErrOut procedure isn't shown. It is my personal debugging alert and is not
- intended for use in a real application. You can have your own. MacApp's is
- better yet. You may want to look at Signals, which is an error recovery
- procedure from MacDTS. Another important point, the main event loop will test
- for the global flag "gCallBackCalled" and then calls ClearChannel(gChan). This
- will dispose of the channel and the snd in use.
-
- Jim Reekes
- Ethics Officer
- Macintosh Developer Technical Support
- Tuesday, November 8, 1988 12:54 PM
-
-
- {------------------------------------------------------------}
- FUNCTION GetNamedSnd(index : INTEGER; VAR sndHdl: Handle;
- VAR sndFormat: INTEGER): OSErr;
- {This is a handy routine that will go fetch a snd resouce from a
- name contained in a string list. This allows us to easily
- change snds without recompiling. Also, it returns the current
- snd format. Any error returned will be the resErr.}
-
- TYPE
- sndRec = RECORD
- format : INTEGER;
- numSynths : INTEGER;
- ResID : INTEGER;
- END;
-
- sndRecPtr = ^sndRec;
- sndRecHandle = ^sndRecPtr;
-
- VAR
- namedSnd : STR255;
-
- BEGIN
- GetIndString (namedSnd, sndStrID, index);
- IF namedSnd <> '' THEN
- sndHdl := GetNamedResource ('snd ', namedSnd);
-
- IF sndHdl <> NIL THEN
- sndFormat := sndRecHandle(sndHdl)^^.format;
-
- GetNamedSnd := ResError; {return the error}
- END;
-
- {------------------------------------------------------------}
- Procedure DoCallBack(theChan: SndChannelPtr; theCmd: SndCommand);
- {This will be called at interrupt time and first gets the A5
- passed to us in the callBackCmd. It sets a global flag to be
- tested in the main event loop.}
-
- VAR
- theA5 : LONGINT;
-
- BEGIN
- theA5 := SetA5(theCmd.param2); {refer to tech note 208}
- gCallBackCalled := TRUE;
- theA5 := SetA5(theA5);
- END;
-
- {------------------------------------------------------------}
- Procedure InstallTheCallBack(theChan: SndChannelPtr);
- {This will install a callBackCmd into the channel and passes
- the application's A5.}
-
- VAR
- myCmd : sndCommand;
- myErr : OSErr;
-
- BEGIN
- WITH myCmd DO BEGIN
- cmd := callBackCmd;
- param1 := 0;
- param2 := SetCurrentA5; {refer to tech note 208}
- END;
-
- myErr := SndDoCommand (theChan, myCmd, FALSE);
- IF myErr <> noErr THEN
- ErrOut(myErr, ' after callBackCmd');
- END;
-
- {------------------------------------------------------------}
- Procedure ClearChannel(VAR theChan: SndChannelPtr);
- {This will wipe out any channel and clear the chanPtr. If the
- channel is in use, it will stop any sound and then clear it.}
-
- VAR
- myErr : OSErr;
-
- BEGIN
- IF (theChan <> NIL) THEN BEGIN
- myErr := SndDisposeChannel(theChan, TRUE);
- IF myErr <> noErr THEN
- ErrOut(myErr, ' after SndDisposeChannel');
- theChan := NIL;
- END;
-
- IF gSndHandle <> NIL THEN BEGIN
- HUnlock (gSndHandle); {we're done with the snd}
- gSndHandle := NIL;
- END;
-
- gCallBackCalled := FALSE;
- END;
-
- {------------------------------------------------------------}
- Procedure AsynchSndPlay;
- {This will call SndPlay. The snd must be either a format 2 or
- format 1 that contains synth information. We use a global
- resouce handle for the snd.}
-
- VAR
- myErr : OSErr;
- sndFormat : INTEGER;
-
- BEGIN
- ClearChannel(gChan);
- myErr := GetNamedSnd(1, gSndHandle, sndFormat);
- IF myErr = noErr THEN BEGIN
-
- {create a channel without linking a synth resouce}
- myErr := SndNewChannel (gChan, 0, 0, @DoCallBack);
- IF myErr = noErr THEN BEGIN
- myErr := SndPlay (gChan, gSndHandle, TRUE);
- IF myErr <> noErr THEN
- ErrOut(myErr, ' after SndPlay');
-
- InstallTheCallBack(gChan);
- END ELSE
- ErrOut(myErr, ' after SndNewChannel');
- END ELSE
- ErrOut(myErr, ' after getting resource');
- END;
-
- {------------------------------------------------------------}
- PROCEDURE PlaySndBuffer;
- {This routine will install a sampled sound from a format 2
- resouce using the soundCmd. This same procedure could
- include a TYPE for the format 1 resource, and then support
- both formats. We use a global resource handle for the snd.
- This snd will have to be locked after a moving it higher
- in the heap. Once the snd is installed, send some notes
- to play a melody.}
-
- TYPE
- snd2Header = RECORD
- format : INTEGER;
- refCount : INTEGER;
- numCmds : INTEGER;
- firstCmd : SndCommand;
- END;
- snd2HdrPtr = ^snd2Header;
-
- VAR
- myErr : osErr;
- myCmd : sndCommand;
- firstCmdPtr : Ptr;
- headerPtr : snd2HdrPtr;
- dataPtr : SoundHeaderPtr; {from Sound.p interface file}
- sizeOfCmds, pitch : longInt;
- theFormat : INTEGER;
-
- BEGIN
- ClearChannel(gChan);
- myErr := GetNamedSnd(2, gSndHandle, sndFormat);
- IF (myErr = noErr) AND (sndFormat = 2) THEN BEGIN
- myErr := SndNewChannel (gChan, sampledSynth, 0, @DoCallBack);
-
- IF myErr = noErr THEN BEGIN {pointer math to find sample}
- MoveHHi(gSndHandle);
- Hlock(gSndHandle);
- headerPtr := snd2HdrPtr(gSndHandle^);
- firstCmdPtr := @headerPtr^.firstCmd;
- sizeOfCmds := headerPtr^.numCmds * SizeOf(sndCommand);
- dataPtr := Pointer(ORD4(firstCmdPtr) + sizeOfCmds);
-
- WITH myCmd DO BEGIN
- cmd := soundCmd;
- param1 := 0;
- param2 := ORD4(DataPtr); {pointer to sample}
- END;
-
- myErr := SndDoCommand(gChan, myCmd, FALSE);
- IF myErr <> noErr THEN
- ErrOut(myErr, 'after do sound cmd');
-
- PlaySomeNotes(gChan); {send some noteCmds}
- InstallTheCallBack(gchan);
- END ELSE
- ErrOut(myErr, ' after sndNewChannel')
- END ELSE
- ErrOut(myErr, ' after getting resource');
- END;
-
-
-
-
-